#include "NextBatch.hpp"

using namespace BFT::messages;

NextBatch::NextBatch(uint16_t _tag, uint32_t _view, boost::shared_ptr<NextBatch> _nb)
{
	NextBatch(_tag, _view, _nb.get()->getSeqNo(), _nb.get()->getHistory(), _nb.get()->getCommands(),
		  _nb.get()->getNonDeterminism(), _nb.get()->getCPDigest(), _nb.get()->getTakeCP(), _nb.get()->getSendingReplica() );
}

NextBatch::NextBatch(uint16_t _tag, uint32_t _view, uint32_t _seq, boost::shared_ptr<HistoryDigest> _hist, 
			boost::shared_ptr<CommandBatch> _batch,	boost::shared_ptr<NonDeterminism> _non, 
			boost::shared_ptr<Digest> _cphash, bool _cp, uint32_t _sendingReplica)
{
	boost::shared_ptr<CertificateEntry> temp_ce; 
	temp_ce.reset(new CertificateEntry(_hist, _batch, _non, _cphash));
	NextBatch(_tag, _view, _seq, temp_ce, _cp, _sendingReplica);
}


NextBatch::NextBatch(uint16_t _tag, uint32_t _view, uint32_t _seq, 
	boost::shared_ptr<CertificateEntry> _entry, bool _cp, uint32_t _sendingReplica)
	: MacArrayMessage::MacArrayMessage ( _tag, computeSize(_view, _seq, _entry, _cp),
					     _sendingReplica, BFT::Parameters::getExecutionCount())
{

//	fprintf(stderr, "****** NextBatch constructor ******\n");
//	MacArrayMessage::printInfo();	
//	fprintf(stderr, "sending replica: %d, computeSize()%d, executionCount()%d\n",
//		_sendingReplica, computeSize(_view, _seq, _entry, _cp), BFT::Parameters::getExecutionCount());

	
	viewNo = _view;
	seqNo = _seq;
	certEntry = _entry;
	takeCP =  _cp;
	
	int offset = getOffset();
	int bytes_size;
	boost::shared_ptr<char> bytes = getBytes(bytes_size);
	
	
	//write down the view number
	*(uint32_t*)(bytes.get()+offset) = htonl((uint32_t)viewNo);
	offset += VIEW_NUMBER_SIZE;
	
	//write down the sequence number
	*(uint32_t*)(bytes.get()+offset) = htonl((uint32_t)seqNo);
	offset += SEQUENCE_NUMBER_SIZE;
	
	//write down the history
	memcpy( (void*)(bytes.get()+offset), (const void*)(getHistory().get()->getBytes().get()) , DIGEST_SIZE );
	offset += DIGEST_SIZE;

	//write down the cphash
	memcpy( (void*)(bytes.get()+offset), (const void*)(getCPDigest().get()->getBytes().get()) , DIGEST_SIZE );
	offset += DIGEST_SIZE;
	
	//write down the checkpoint flag
	*(uint16_t*)(bytes.get()+offset) = htons( (uint16_t)(takeCP?1:0) );
	offset += CHECKPOINT_FLAG_SIZE;
	
	//write down the nondeterminism size
	int non_determinism_size = getNonDeterminism().get()->getSize();
	*(uint16_t*)(bytes.get()+offset) = htons( (uint16_t) non_determinism_size );
	offset += NON_DETERMINISM_SIZE_SIZE;
	
	//write down the non determinism
	memcpy( (void*)( bytes.get()+offset ) , (const void *)(getNonDeterminism().get()->getBytes().get()) , non_determinism_size );
	offset += non_determinism_size;
	
	//write down the size of the command batch
	int temp_commands_size;
	uint32_t command_batch_size;
	boost::shared_ptr<char> command_batch = getCommands().get()->getBytes(temp_commands_size);
	command_batch_size = (uint32_t)temp_commands_size;
	*(uint32_t*)(bytes.get()+offset) = htonl(command_batch_size);
	offset += NB_COMMAND_BATCH_SIZE_SIZE;
	
	//write down the entry count
	*(uint16_t*)(bytes.get()+offset) = htons( (uint16_t)(getCommands().get()->getEntryCount()) );
	offset +=  ENTRY_COUNT_SIZE;
	
	//write down the command batch
	memcpy( (void*)(bytes.get()+offset) , (const void *)(command_batch.get()) , command_batch_size);
	offset += command_batch_size;
}

NextBatch::NextBatch(boost::shared_ptr<char> _bytes, int _bytes_size)
	: MacArrayMessage::MacArrayMessage(_bytes, _bytes_size)		
{
	unsigned int offset = getOffset();
//	fprintf(stderr, "constructing a next batch from bytes\n");
	
	//read the view number
	viewNo = ntohl( *(uint32_t*)(_bytes.get()+offset) );
	offset += VIEW_NUMBER_SIZE;
	
	//read the sequence number
	seqNo = ntohl( *(uint32_t*)(_bytes.get()+offset) );
	offset += SEQUENCE_NUMBER_SIZE;
	
	//read the history digest
	boost::shared_ptr<char> hd_bytes ( (char*)malloc(DIGEST_SIZE*sizeof(char)), free );
	memcpy( (void*)(hd_bytes.get())  , (const void *)(_bytes.get()+offset) , DIGEST_SIZE); 
	offset += DIGEST_SIZE;
	boost::shared_ptr<HistoryDigest> history;
	history = HistoryDigest::fromBytes(hd_bytes, DIGEST_SIZE);

	//read the cphash
	boost::shared_ptr<char> cph_bytes ( (char*)malloc(DIGEST_SIZE*sizeof(char)), free );
	memcpy( (void*)(cph_bytes.get()) , (const void *)(_bytes.get()+offset) , DIGEST_SIZE);
	offset += DIGEST_SIZE;
	boost::shared_ptr<Digest> cphash;
	cphash = Digest::fromBytes(cph_bytes, DIGEST_SIZE);
	
	//read the checkpoint flag
	uint16_t temp_cp = ntohs( *(uint16_t*)(_bytes.get()+offset) ) ;
	offset += CHECKPOINT_FLAG_SIZE;
	takeCP = (temp_cp == 1);
	
	//read the nondeterminism size
	uint16_t nondet_size = ntohs( *(uint16_t*)(_bytes.get()+offset) );
	offset += NON_DETERMINISM_SIZE_SIZE;
	
	//read the nondeterminism
	boost::shared_ptr<char> nondet_bytes ( (char*)malloc(nondet_size*sizeof(char)) , free);
	memcpy( (void*)(nondet_bytes.get()) , (const void *)(_bytes.get() + offset) , nondet_size);
	offset += nondet_size;
	boost::shared_ptr<NonDeterminism> nondet (new NonDeterminism(nondet_bytes, nondet_size));
	
	//read the command batch size
	uint32_t cb_size  = ntohl( *(uint32_t*)(_bytes.get()+offset) );
	offset += NB_COMMAND_BATCH_SIZE_SIZE;
	
	//read the number of entries
	uint16_t cb_entry = ntohs( *(uint16_t*)(_bytes.get()+offset) ) ;
	offset += ENTRY_COUNT_SIZE;
	
	//read the command batch
	boost::shared_ptr<char> cb_bytes ( (char*)malloc(cb_size*sizeof(char)) , free );
	memcpy( (void *)(cb_bytes.get()) , (const void *)(_bytes.get()+offset) , cb_size);
	offset += cb_size;
	boost::shared_ptr<CommandBatch> commands ( new CommandBatch( cb_bytes, cb_size, cb_entry));
	
	certEntry.reset( new CertificateEntry(history, commands, nondet, cphash) );
	
	int temp_size;
	boost::shared_ptr<char> temp_bytes;	
	temp_bytes = getBytes(temp_size);		
	if (offset != temp_size - getAuthenticationSize())
	{
		fprintf(stderr,"something has gone wrong in the creation of a NextBatch (condtructor from bytes): %d != %d - %d \n",
			 offset, temp_size,getAuthenticationSize());
		exit(68);
	}	
}

bool NextBatch::matches(boost::shared_ptr<NextBatch> _nb)
{
	return _nb.use_count()!=0 && _nb.get()->getSeqNo() == seqNo && _nb.get()->getView() == viewNo 
		&& getHistory().get()->equals(_nb.get()->getHistory()) && _nb.get()->getTakeCP() == takeCP
		&& getNonDeterminism().get()->equals(_nb.get()->getNonDeterminism())
		&& getCommands().get()->equals(_nb.get()->getCommands());
		
}

bool NextBatch::equals(boost::shared_ptr<NextBatch> _nb)
{
	return MacArrayMessage::equals(_nb) && matches (_nb);
}

//TODO remove a lot of useless parameters
int NextBatch::computeSize(uint32_t _view, uint32_t _seq, boost::shared_ptr<CertificateEntry> _entry, bool _cp)
{

	return VIEW_NUMBER_SIZE +						// 4		
	       SEQUENCE_NUMBER_SIZE +						// 4
	       DIGEST_SIZE +							// 16
	       DIGEST_SIZE + 							// 16
	       CHECKPOINT_FLAG_SIZE +						// 2
	       NON_DETERMINISM_SIZE_SIZE +					// 2
	       _entry.get()->getNonDeterminism().get()->getSize() +		// 8
	       NB_COMMAND_BATCH_SIZE_SIZE +					// 4
	       ENTRY_COUNT_SIZE +						// 2
	       _entry.get()->getCommandBatch().get()->getSize();       		// variable (28) = 70	
}

void NextBatch::printInfo()
{
	fprintf(stdout,"NextBatch information:\n");
	fprintf(stdout,"View number: %d\nSequence number %d\nTakeCP : ",viewNo, seqNo);
	if(takeCP)
		fprintf(stdout,"true\n");
	else	
		fprintf(stdout,"false\n");
	certEntry.get()->printInfo();
}

NextBatch::NextBatch()//dummy constructor called only by subclassess before calling the real constructor
{
}
